/**
 * e.g:
 * @BindComponentMethodThis()
 * class Table extends React.Component<any,any>{
 *      @HandleAsyncInfo({
 *          loading: '获取列表中，请稍候...',
 *          // 会在 this.state 上创建建名字为 xxxState 的字段
 *          // 该字段的值为该异步方法的状态(ASYNC_STATE枚举)，当异步方法发生改变时会自动设置这个字段的值，并调用setState
 *          setAsyncStateName: 'xxxState',
 *          // 异步状态变化回调，回调函数的this为当前实例
 *          stateChangeCallback:function(this: Table,state: ASYNC_STATE){
 *              // todo
 *          }
 *      })
 *      public async renderList(){
 *          let response = await fetch('xxxx.json');
 *          if (!response.ok) {
 *              throw '获取列表失败';
 *          }
 *          let data = await respone.json();
 *          // todo ...
 *          return '执行成功';
 *      }
 *      public render(){
 *          return (<button onClick={this.renderList}>render<button>)
 *      }
 * }
 */

import { aopGenerator, isPromise, isDecorateClass, isFunction } from './Aop';
import { message } from 'antd';
import { setNamespaceValue } from '../tool';

/**
 * 处理异步过程中的信息提示及设置状态
 * 
 * @export
 * @param {IHandAsyncInfo} [{success = null, loading = null, error = null, setAsyncStateName = null}={}]
 * @returns
 */
export function HandleAsyncInfo({
    success = null,
    loading = null,
    error = null,
    setAsyncStateName = null,
    stateChangeCallback = null
}: IHandAsyncInfo = {}) {
    return function (_target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
        return aopGenerator(_target, descriptor, function (target, self, args: any[]) {
            let promise: Promise<Error> = null,
                close: Function = null,
                {isClass} = isDecorateClass(_target, descriptor),
                className = isClass ? (target as Function).name : (_target.constructor as Function).name,
                setAsyncState = function (asyncState: ASYNC_STATE, err: IAsyncError<any> = null): boolean {
                    let componentState = self.state || {}, handle = false;
                    if (setAsyncStateName && typeof componentState === 'object') {
                        setNamespaceValue(componentState, setAsyncStateName.split('.'), asyncState);
                    }
                    // 触发state
                    if (isFunction(self.setState)) {
                        self.setState(componentState);
                    }
                    if (isFunction(stateChangeCallback)) {
                        handle = !!(stateChangeCallback.call(self, asyncState, err));
                    }
                    return handle;
                };

            // 设置异步状态为 loading ....
            if (loading) {
                close = message.loading(loading, 0);
            }
            try {
                promise = (target as Function).apply(self, args);
                setAsyncState(ASYNC_STATE.LOADING);
            } catch (e) {
                promise = Promise.reject<Error>(e);
            }

            if (isPromise(promise)) {
                promise.then(function (value) {
                    // showSuccess 为 true 表示 stateChangeCallback 已经处理这个状态了，不需要显示信息
                    let showSuccess = !setAsyncState(ASYNC_STATE.SUCCESS);
                    afterClose(close, () => {
                        if (showSuccess) {
                            if (success) {
                                message.success(success);
                            } else if (value) {
                                message.success(value.message ? value.message : value);
                            }
                        }
                    });
                }, function <T>(reason: IAsyncError<T>) {
                    // showError 为 true 表示 stateChangeCallback 已经处理这个状态了，不需要显示信息
                    let showError = !setAsyncState(ASYNC_STATE.ERROR, reason);
                    afterClose(close, () => {
                        if (showError) {
                            if (error) {
                                message.error(error);
                            } else if (reason) {
                                message.error(reason.message ? reason.message : reason);
                            }
                        }
                        throw reason;
                    });
                });
            } else {
                setAsyncState(null);
                afterClose(close);
                throw new TypeError(`HandleFetchInfo必须装饰一个异步方法,${className}->${propertyKey || ''}不是一个异步方法。`);
            }
            return promise;
        });
    } as any;
}
export enum ASYNC_STATE {
    LOADING = 1,
    SUCCESS,
    ERROR
};
export interface IHandAsyncInfo {
    success?: string;
    loading?: string;
    error?: string;
    setAsyncStateName?: string;
    stateChangeCallback?: <T>(currentState: ASYNC_STATE, err?: IAsyncError<T>) => void;
};
export interface IAsyncError<T> {
    message?: string;
    data?: T
};
function afterClose(close: Function, callback: Function = null, delay: number = 600) {
    if (!close) {
        doAfterClose();
    } else {
        close();
        callback && setTimeout(() => {
            doAfterClose();
        }, delay);
    }
    function doAfterClose() {
        if (callback) {
            callback.call(null);
        }
    }
}

